
/**
 * AppleIIGo
 * 6502 definitions
 * (C) 2007 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#define FLAG6502_C (1 << 0)
#define FLAG6502_Z (1 << 1)
#define FLAG6502_I (1 << 2)
#define FLAG6502_D (1 << 3)
#define FLAG6502_B (1 << 4)
#define FLAG6502_V (1 << 6)
#define FLAG6502_N (1 << 7)

inline void setN(bool b) {if (b) P |= FLAG6502_N; else P &= ~FLAG6502_N;}
inline void setV(bool b) {if (b) P |= FLAG6502_V; else P &= ~FLAG6502_V;}
inline void setB(bool b) {if (b) P |= FLAG6502_B; else P &= ~FLAG6502_B;}
inline void setD(bool b) {if (b) P |= FLAG6502_D; else P &= ~FLAG6502_D;}
inline void setI(bool b) {if (b) P |= FLAG6502_I; else P &= ~FLAG6502_I;}
inline void setZ(bool b) {if (b) P |= FLAG6502_Z; else P &= ~FLAG6502_Z;}
inline void setC(bool b) {if (b) P |= FLAG6502_C; else P &= ~FLAG6502_C;}
inline UINT32 getN() {return P & FLAG6502_N;}
inline UINT32 getV() {return P & FLAG6502_V;}
inline UINT32 getB() {return P & FLAG6502_B;}
inline UINT32 getD() {return P & FLAG6502_D;}
inline UINT32 getI() {return P & FLAG6502_I;}
inline UINT32 getZ() {return P & FLAG6502_Z;}
inline UINT32 getC() {return P & FLAG6502_C;}

/**
 * Fast condition operations for N, Z and C
 *
 * For independent N and Z setting, bit 17 of fastCondNZ also represents N
 */
inline UINT32 fastCondIsN() {
	return (fastCondNZ & 0x280);
}

inline UINT32 fastCondIsZ() {
	return !(fastCondNZ & 0xff);
}

inline UINT32 fastCondIsC() {
	return (fastCondC & 0x100);
}

inline void fastCondSetNZ(bool n, bool z) {
	fastCondNZ = ((n << 9) | !z);
}

inline void fastCondSetC(bool c) {
	fastCondC = (c << 8);
}

inline UINT32 getP() {
	setN(fastCondIsN());
	setZ(fastCondIsZ());
	setC(fastCondIsC());
	return P;
}

inline void setP(UINT8 P) {
	this->P = P | 0x20; // P bit 5 is always set
	fastCondSetC(getC());
	fastCondSetNZ(getN(), getZ());
}

inline UINT8 readByte8(UINT8 address) {
	return mmu->readMemory((UINT32) address);
}

inline void writeByte8(UINT8 address, UINT8 value) {
	mmu->writeMemory((UINT32) address, value);
}

inline UINT8 readByte16(UINT16 address) {
	return mmu->readMemory((UINT32) address);
}

inline void writeByte16(UINT16 address, UINT8 value) {
	mmu->writeMemory((UINT32) address, value);
}

inline UINT16 readWord8(UINT8 address) {
	return mmu->readMemory((UINT32) address) | 
		(mmu->readMemory((UINT32) (address + 1)) << 8);
}

inline UINT16 readWord16(UINT16 address) {
	return mmu->readMemory((UINT32) address) |
		(mmu->readMemory((UINT32) (address + 1)) << 8);
}

inline void push(UINT8 value) {
	writeByte16(0x100 | S, value);
	S--;
}

inline UINT8 pop() {
	S++;
	return readByte16(0x100 | S);
}

// Effective address base modes 
inline UINT8 eaimm() {
	UINT8 address = readByte16(PC);
	PC++;
	return address;
}

inline UINT8 eazp() {
	UINT8 address = readByte16(PC);
	PC++;
	return address;
}

inline UINT8 eazpx() {
	UINT8 address = readByte16(PC) + X;
	PC++;
	return address;
}

inline UINT8 eazpy() {
	UINT8 address = readByte16(PC) + Y;
	PC++;
	return address;
}

inline UINT16 eaabs() {
	UINT16 address = readWord16(PC);
	PC += 2;
	return address;
}

inline SINT16 earel() {
	SINT16 address = (SINT16) readWord16(PC);
	PC++;
	return address;
}

// Address calculation with page crossing
inline UINT16 addWithPageCrossing(UINT16 address, short offset) {
	UINT16 addressPageCrossing = address + offset;
	if ((addressPageCrossing ^ address) & 0xff00) 
		cycleIndex++;
	return addressPageCrossing;
}

// Effective address composed modes
inline UINT16 eaabsx() {
	return eaabs() + X;
}

inline UINT16 eaabsxWithPageCrossing() {
	return addWithPageCrossing(eaabs(), X);
}

inline UINT16 eaabsy() {
	return eaabs() + Y;
}

inline UINT16 eaabsyWithPageCrossing() {
	return addWithPageCrossing(eaabs(), Y);
}

inline UINT16 eaabsindWithPageCrossing() {
	UINT16 address = addWithPageCrossing(eaabs(), 0);
	return readWord16(address);
}

inline UINT16 eazpxind() {
	UINT16 address = eazpx();
	return readWord8(address);
}

inline UINT16 eazpindy() {
	UINT8 address8 = eaimm();
	UINT16 address = readWord8(address8);
	return address + Y;
}

inline UINT16 eazpindyWithPageCrossing() {
	UINT8 address8 = eaimm();
	UINT16 address = readWord8(address8);
	return addWithPageCrossing(address, Y);
}

// Effective address 65C02 modes 
inline UINT16 eazpind() {
	UINT8 address = eazp();
	return readWord8(address);
}

inline UINT16 eaabsxindWithPageCrossing() {
	UINT16 address = addWithPageCrossing(eaabs(), X);
	return readWord16(address);
}

// CPU xxxxxx01 instructions
inline void opOra(UINT8 operand) {
	A |= operand;
	fastCondNZ = A;
}

inline void opAnd(UINT8 operand) {
	A &= operand;
	fastCondNZ = A;
}

inline void opEor(UINT8 operand) {
	A ^= operand;
	fastCondNZ = A;
}

inline void opAdc(UINT8 operand) {
	bool v = !((A ^ operand) & 0x80);
	if (getD()) {
		cycleIndex++;
		fastCondC = (A & 0x0f) + (operand & 0x0f) + (fastCondC >> 8);
		if (fastCondC >= 0x0a)
			fastCondC = 0x10 | ((fastCondC + 6) & 0xf);
		fastCondC += (A & 0xf0) + (operand & 0xf0);
		if (fastCondC >= 0xa0) {
			if (fastCondC >= 0x180)
				v = false;
			fastCondC += 0x60;
			fastCondC &= 0xff;
			fastCondC |= 0x100;
		} else {
			if (fastCondC < 0x80)
				v = false;
		}
	} else {
		fastCondC = A + operand + (fastCondC >> 8);
		if ((fastCondC >= 0x180) || (fastCondC < 0x80))
			v = false;
	}
	setV(v);
	fastCondNZ = A = fastCondC;
}

inline void opLda(UINT8 operand) {
	fastCondNZ = A = operand;
}

inline void opCmp(UINT8 operand) {
	fastCondNZ = fastCondC = A - operand + 0x100;
}

inline void opSbc(UINT8 operand) {
	bool v = ((A ^ operand) & 0x80);
	if (getD()) {
		cycleIndex++;
		fastCondNZ = 0x0f + (A & 0x0f) - (operand & 0x0f) + (fastCondC >> 8);
		if (fastCondNZ < 0x10) {
			fastCondC = 0;
			fastCondNZ -= 0x06;
		} else {
			fastCondC = 0x10;
			fastCondNZ -= 0x10;
		}
		fastCondC += 0xf0 + (A & 0xf0) - (operand & 0xf0);
		if (fastCondC >= 0x100) {
			if (fastCondC >= 0x180)	v = false;
		} else {
			if (fastCondC < 0x80) v = false;
			fastCondC -= 0x60;
			fastCondC &= 0xff;
		}
		fastCondC = (fastCondC + fastCondNZ) & 0xff | (fastCondC & 0x100);
	} else {
		fastCondC = 0xff + A - operand + (fastCondC >> 8);
		if ((fastCondC >= 0x180) || (fastCondC < 0x80))
			v = false;
	}
	setV(v);
	fastCondNZ = A = fastCondC;
}

// CPU xxxxxx10 instructions
inline UINT8 opAsl(UINT32 operand) {
	return (fastCondNZ = fastCondC = (operand << 1));
}

inline UINT8 opRol(UINT32 operand) {
	return (fastCondNZ = fastCondC = (operand << 1) | (fastCondC >> 8));
}

inline UINT8 opLsr(UINT32 operand) {
	fastCondC = (operand & 1) << 8;
	return (fastCondNZ = (operand >> 1));
}

inline UINT8 opRor(UINT32 operand) {
	return (fastCondNZ = fastCondC = 
		((operand & 1) << 8) | (((fastCondC & 0x100) | operand) >> 1));
}

inline void opLdx(UINT8 operand) {
	fastCondNZ = X = operand;
}

inline void opLdy(UINT8 operand) {
	fastCondNZ = Y = operand;
}

inline UINT8 opDec(UINT8 operand) {
	return (fastCondNZ = (operand + 0xff));
}

inline UINT8 opInc(UINT8 operand) {
	return (fastCondNZ = (operand + 0x01));
}

// CPU xxxxxx00 instructions
inline void opBit(UINT8 operand) {
	setV(operand & 0x40);
	fastCondNZ = ((operand & 0x80) << 2) | ((operand & A) != 0);
}

inline void opCpy(UINT32 operand) {
	fastCondNZ = fastCondC = Y - operand + 0x100;
}

inline void opCpx(UINT32 operand) {
	fastCondNZ = fastCondC = X - operand + 0x100;
}

// CPU xxx10000 instructions
inline void opBranch(SINT8 operand, bool isBranch) {
	if (isBranch) {
		PC = addWithPageCrossing(PC, operand);
		cycleIndex++;
	}
}

// 65C02 instructions
inline UINT8 opTsb(UINT8 operand) {
	opBit(operand);
	return (operand | A);
}

inline UINT8 opTrb(UINT8 operand) {
	opBit(operand);
	return (operand & ~A);
}

// Stack PC
inline void pushPc() {
	push(PC >> 8);
	push(PC);
} 

inline void popPc() {
	PC = pop();
	PC |= pop() << 8;
}

// Check IRQ line
inline void checkIrq() {
	if (irqAssertedCount && !getI()) {
		pushPc();
		setB(false);
		push(getP());
		setI(true);
		PC = readWord16(0xfffe);
		cycleIndex += 7;
	}
}
